/*

Panda JavaScript Components
http://panda-js.org/

Thank you to YUI, jQuery, and modular.js for the amazing libraries which have 
inspired this work.

http://developer.yahoo.com/yui/
http://jquery.com/
http://code.google.com/p/modular-js/

*/

window.panda = (function () {
    
    var util, app, Module;
    
    /** 
     * A generic module
     * @see http://panda-js.org/modules/Module
     */
    Module = function (name) {
        this.imported = {};
        this.self = {};
        this.name = function () {
            return name;
        };
    };
    
    Module.prototype = {
        /**  The module's API */
        self : null,
        
        /** A list of all imported modules */
        imported : null,
        
        /** An accessor for the module's name as a string */
        name : null,
        
        /** Imports existing modules as sub-modules */
        use : function () {
            var i = 0,
                l = arguments.length,
                imported = this.imported,
                module;
                
            for (i ; i < l; i++) {
                module = arguments[i];
                imported[module] = app.module(module);
            }
            
            return this;
        },
        
        /** Defines the module API from the supplied module's return value */
        define : function (module) {
            this.self = module.apply(this, util.values(this.imported));
            return this;
        },
        
        /** Publicly exposes the module */
        expose : function () {
            util.namespace(this.name(), this.self);
        }
    };
    
    util = {
        /** Extracts the values from an array or object */
        values : function (object) {
            var out = [];
            
            panda.each(object, function (value) {
                out.push(value);
            });
            
            return out;
        }, 
        
        /** Extracts the keys from an array or object */
        keys : function (object) {
            var out = [];
            
            panda.each(object, function (value, key) {
                out.push(key);
            });
            
            return out;
        },
        
        /** Safely creates a public namespace object */
        namespace : function (namespace, value) {
            var value = value || {},
                components = namespace.split(/[^a-z0-9]+/ig),
                context = window,
                i, l, last,
                component;

            for (i = 0, l = components.length; i < l; i += 1) {
                last = i === (l - 1);
                component = components[i];

                if (!context[component]) {
                    context[component] = (last) ? value : {};
                }

                context = context[component];
            }

            return context;
        },
        
        /** Extends an object by copying members */
        extend : function (from, to) {
            for (var i in from) {
                to[i] = to[i] || from[i];
            }
            
            return to;
        }, 
        
        /** A generic object or array iterator */
        each : function (object, callback) {
            var i, l;
            
            if (!!object.length && !!object[0]) {
                for (i = 0, l = object.length; i < l; i++) {
                    callback(object[i], i, object);
                }
            }
            else {
                for (i in object) {
                    callback(object[i], i, object);
                }
            }
        },
        
        /** Generic script loader */
        require : function (path, callback) {
            var script = document.createElement('script'),
                target = document.body || document.documentElement;
                
            script.src = path;
            
            if (!!document.all && !!callback) script.onreadystatechange = function () {
                if (this.readyState === 'complete') callback();
            };
            else script.onload = callback;
            
            target.appendChild(script);
        }
    };
    
    app = {
        /** Module Registry */
        registry : {},
        
        /** Auto-runs a Panda JS application */
        autoRun : function () {
            var scriptNode = document.getElementById('panda-js');
            if (!scriptNode) return;
            
            if (document.all) scriptNode.onreadystatechange = function () {
                if (this.readyState === 'complete') eval(this.innerHTML);
            };
            else scriptNode.onload = function () {
                eval(scriptNode.innerHTML);
            };
        },
        
        /** Module Factory */
        module : function (name) {
            var registry = app.registry,
                module   = registry[name];
            
            if (!module) {
                module = new Module(name);
                app.registry[name] = module;
            }
            
            return module;
        }
    };
    
    // Attempt to auto-run the app
    app.autoRun();
    
    // Expose everything, copying util.* to a wrapper function for the module 
    // factory.
    return util.extend(util, function (name) {
        return app.module(name);
    });
    
}());